Skip to content

Make the records append rollback-safe and validate record shapes - #207

Merged
guijacquemet merged 2 commits into
mainfrom
claude/records-append-rollback
Nov 7, 2026
Merged

guijacquemet merged 2 commits into
mainfrom
claude/records-append-rollback

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

Fixes #206

What changed and why

  • _append_records no longer appends to the live morphobricks.records.jsonl. It builds old bytes plus new lines, writes them to a synced temporary file in the same folder, and replaces the live file in one atomic replace. A failure in write, flush or fsync leaves the live file untouched and removes the temporary file.
  • publish_description now puts back both the description and the records bytes (or removes the records file if there was none) when the append fails.
  • read_records refuses any line that is not one of the Spec record types (reconfirmation, image_count_changed, removed) with exactly that type's key set (Spec main 964f097, docs/experiment-v1.md section 4.6). {} was accepted before.
  • Reader rule for a record whose SHA does not match the published file: the hashes are history. Later publications change the file without a record, so an older record rightly names a hash that is no longer the published one. A reader never compares them with the published file and never drops or trusts a record by them. Written in the module docstring and covered by a test. This is my reading of the Spec; please confirm.
  • tests/test_spec_encoding.py: the non-ASCII record now carries all keys of a reconfirmation record (it was an incomplete record that the new validation refuses).

The durability of the records write is as before (temporary file is fsynced before the replace); the directory is not fsynced, as for the description.

Regression tests (tests/test_description_file.py)

On the old code (origin/main source, new tests): 12 failed, 23 passed. On the new code: all 35 pass.

Failing on old code:

  • test_a_write_that_fails_inside_the_append_leaves_file_and_records_as_before (4 cases: no records file or existing records, times partial bytes or full bytes written before the failure)
  • test_a_failed_fsync_of_the_records_leaves_file_and_records_as_before
  • test_a_records_file_that_is_not_lines_of_spec_records_is_refused (7 new cases: {}, type only, unknown type, no type, extra key, renamed key, partial removed)

Also added test_a_record_whose_hash_is_not_the_published_file_is_history_not_an_error (passes on both).

Checks


Generated by Claude Code

The records file is now replaced atomically (old bytes plus new lines, written
and synced in a temporary file) and a failed publication restores both the
description and the records. read_records refuses any line that is not one of
the three Spec record types with exactly that type's keys.

Fixes #206

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm

# Conflicts:
#	CHANGELOG.md
@guijacquemet
guijacquemet merged commit ad55dd4 into main Nov 7, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/records-append-rollback branch November 7, 2026 23:18
guijacquemet pushed a commit that referenced this pull request Nov 7, 2026
main now validates each record against the Spec record shapes (#207),
so the four-key record this test wrote was refused and nothing was run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Records append is not rollback-safe; read_records accepts any JSON object

2 participants